﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Windows.Forms;
using DirectShowLib;

namespace XP.Utilities.Utility
{
    public enum PlayState
    {
        Stopped,
        Paused,
        Running,
        Init
    }
    public class Camera
    {
        private int _width;
        private int _height;

        public PlayState CurrentState = PlayState.Stopped;
        const int D = 0X8000;
        public const int WM_GRAPHNOTIFY = D + 1;

        public IVideoWindow VideoWindow = null;
        public IntPtr Handle = IntPtr.Zero;
        IMediaControl MediaControl = null;
        IMediaEventEx MediaEventEx = null;
        IGraphBuilder GraphBuilder = null;
        ICaptureGraphBuilder2 CaptureGraphBuilder = null;

        DsROTEntry rot = null;

        #region 构造函数
        public Camera()
        {
            
        }

        public Camera(int width, int height)
        {
            this._width = width;
            this._height = height;
        }
        #endregion

        #region 开始捕获
        public void CaptureVideo(IntPtr hwnd)
        {
            int hr = 0;
            IBaseFilter sourceFilter = null;
            try
            {
                GetInterfaces(hwnd);

                hr = this.CaptureGraphBuilder.SetFiltergraph(this.GraphBuilder);
                DsError.ThrowExceptionForHR(hr);

                sourceFilter = FindCaptureDevice();

                hr = this.GraphBuilder.AddFilter(sourceFilter, "Video Capture");
                DsError.ThrowExceptionForHR(hr);

                hr = this.CaptureGraphBuilder.RenderStream(PinCategory.Preview, MediaType.Video, sourceFilter, null, null);
                DsError.ThrowExceptionForHR(hr);

                Marshal.ReleaseComObject(sourceFilter);

                SetupVideoWindow(hwnd, this._width, this._height);

                rot = new DsROTEntry(this.GraphBuilder);

                hr = this.MediaControl.Run();
                DsError.ThrowExceptionForHR(hr);

                this.CurrentState = PlayState.Running;

                this.VideoWindow.get_Owner(out Handle);
            }
            catch (Exception ex)
            {
                MessageBox.Show("发生错误: " + ex.Message,"错误提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        #endregion

        #region 设置驱动接口
        private void GetInterfaces(IntPtr hwnd)
        {
            int hr = 0;
            this.GraphBuilder = (IGraphBuilder)new FilterGraph();
            this.CaptureGraphBuilder = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
            this.MediaControl = (IMediaControl)this.GraphBuilder;
            this.VideoWindow = (IVideoWindow)this.GraphBuilder;
            this.MediaEventEx = (IMediaEventEx)this.GraphBuilder;
            hr = this.MediaEventEx.SetNotifyWindow(hwnd, WM_GRAPHNOTIFY, IntPtr.Zero);
            DsError.ThrowExceptionForHR(hr);
        }
        #endregion

        #region 查找摄像头驱动
        private IBaseFilter FindCaptureDevice()
        {
            int hr = 0;
            UCOMIEnumMoniker classEnum = null;
            UCOMIMoniker[] moniker = new UCOMIMoniker[1];
            object source = null;
            ICreateDevEnum devEnum = (ICreateDevEnum)new CreateDevEnum();
            hr = devEnum.CreateClassEnumerator(FilterCategory.VideoInputDevice, out classEnum, 0);

            DsError.ThrowExceptionForHR(hr);
            Marshal.ReleaseComObject(devEnum);
            if (classEnum == null)
            {
                throw new ApplicationException("未检测到摄像头驱动.");
            }
            int r = 0;
            if (classEnum.Next(moniker.Length, moniker, out r) == 0)
            {
                Guid iid = typeof(IBaseFilter).GUID;
                moniker[0].BindToObject(null, null, ref iid, out source);
            }
            else
            {
                throw new ApplicationException("不能访问摄像头驱动!");
            }
            Marshal.ReleaseComObject(moniker[0]);
            Marshal.ReleaseComObject(classEnum);
            return (IBaseFilter)source;
        }
        #endregion

        #region 启动摄像头捕获
        private void SetupVideoWindow(IntPtr hwnd, int width, int height)
        {
            int hr = this.VideoWindow.put_Owner(hwnd);
            DsError.ThrowExceptionForHR(hr);

            hr = this.VideoWindow.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren);
            DsError.ThrowExceptionForHR(hr);

            ResizeVideoWindow(width, height);

            hr = this.VideoWindow.put_Visible(OABool.True);
            DsError.ThrowExceptionForHR(hr);
        }
        #endregion

        #region 设置捕获状态
        public void ChangePreviewState(bool showVideo)
        {
            int hr = 0;
            if (this.MediaControl == null)
            {
                return;
            }
            if (showVideo == true)
            {
                if (!(this.CurrentState == PlayState.Running))
                {
                    hr = this.MediaControl.Run();
                    this.CurrentState = PlayState.Running;
                }
            }
            else
            {
                hr = this.MediaControl.StopWhenReady();
                this.CurrentState = PlayState.Stopped;
            }
        }
        #endregion

        #region 设置Video大小
        public void ResizeVideoWindow(int width, int height)
        {
            if ((this.VideoWindow != null))
            {
                this.VideoWindow.SetWindowPosition(0, 0, width, height);
            }
        }
        #endregion

        public void HandleGraphEvent()
        {
            int hr = 0;
            EventCode evCode = default(EventCode);
            int evParam1 = 0;
            int evParam2 = 0;
            if (this.MediaEventEx == null)
            {
                return;
            }
            while (this.MediaEventEx.GetEvent(out evCode, out evParam1, out evParam2, 0) == 0)
            {
                hr = this.MediaEventEx.FreeEventParams(evCode, evParam1, evParam2);

                DsError.ThrowExceptionForHR(hr);
            }
        }

        #region 释放占用的资源
        public void Dispose()
        {
            ////stop previewing data
            if ((this.MediaControl != null))
            {
                this.MediaControl.StopWhenReady();
            }

            this.CurrentState = PlayState.Stopped;

            ////stop recieving events
            if ((this.MediaEventEx != null))
            {
                this.MediaEventEx.SetNotifyWindow(IntPtr.Zero, WM_GRAPHNOTIFY, IntPtr.Zero);
            }

            if ((this.VideoWindow != null))
            {
                this.VideoWindow.put_Visible(OABool.False);
                this.VideoWindow.put_Owner(IntPtr.Zero);
            }

            // // Remove filter graph from the running object table
            if ((rot != null))
            {
                rot.Dispose();
                rot = null;
            }

            //// Release DirectShow interfaces
            Marshal.ReleaseComObject(this.MediaControl);
            this.MediaControl = null;
            Marshal.ReleaseComObject(this.MediaEventEx);
            this.MediaEventEx = null;
            Marshal.ReleaseComObject(this.VideoWindow);
            this.VideoWindow = null;
            Marshal.ReleaseComObject(this.GraphBuilder);
            this.GraphBuilder = null;
            Marshal.ReleaseComObject(this.CaptureGraphBuilder);

            this.CaptureGraphBuilder = null;
        }
        #endregion
    }
}
